// typecheck.h
#ifndef TYPECHECK_H
#define TYPECHECK_H

#include "ASTNode.h"
#include <vector>
#include <string>
#include <unordered_map>
#include <stdexcept>
#include <memory>

// 自定义语义错误异常
class SemanticError : public std::runtime_error {
public:
    explicit SemanticError(const std::string& message)
        : std::runtime_error(message) {}
};

// 符号类型
enum class SymbolKind {
    VARIABLE,
    FUNCTION
};

// 符号信息
struct SymbolInfo {
    std::string name;
    SymbolKind kind;
    std::string type; // "int" or "void"

    // 仅用于函数
    std::vector<std::string> paramTypes;
};

class TypeChecker {
public:
    void check(ASTNode* root);

private:
    // 作用域管理
    std::vector<std::unordered_map<std::string, SymbolInfo>> scopes;
    void enterScope();
    void exitScope();
    void addSymbol(const SymbolInfo& symbol);
    SymbolInfo* lookupSymbol(const std::string& name);

    // 状态跟踪
    int loopDepth = 0;
    SymbolInfo* currentFunction = nullptr;

    // AST 遍历/访问者方法
    void visit(ASTNode* node);
    void visitCompUnit(ASTNode* node);
    void visitFuncDef(ASTNode* node);

    // 语句访问者，返回bool表示该语句是否保证有返回（对于一个函数需要递归检查每一个path都要有返回）
    bool visitStmt(ASTNode* node);
    bool visitBlock(ASTNode* node);
    bool visitIfStmt(ASTNode* node);
    bool visitWhileStmt(ASTNode* node);
    bool visitReturnStmt(ASTNode* node);
    bool visitBreakStmt(ASTNode* node);
    bool visitContinueStmt(ASTNode* node);
    bool visitAssignStmt(ASTNode* node);
    bool visitDeclAssignStmt(ASTNode* node);
    bool visitEmptyStmt(ASTNode* node);
    
    // 表达式访问者，返回表达式的类型 ("int", "void")
    std::string visitExpr(ASTNode* node);
    std::string visitBinaryExpr(ASTNode* node); // Add, Mul, Rel, LAnd, LOr
    std::string visitUnaryExpr(ASTNode* node);
    std::string visitVarRef(ASTNode* node);
    std::string visitNumLiteral(ASTNode* node);
    std::string visitFuncCall(ASTNode* node);
};

#endif // TYPECHECK_H


/*
类型检查调用示例（main函数中）：
TypeChecker typeChecker;
typeChecker.check(root);

如果出现不合语言规范的问题，会抛出SemanticError类型的异常
可利用try-catch语句去处理异常，比如把message输出到日志上然后退出程序等等
*/
